package monsmon.util;

import java.awt.Color;
import monsmon.term.*;


/*
 * Contains various terminal-implementation agonositic
 * utility functions and constants
 *	
 *	Constants:
 *		various non-keyboard unicode chars
 *		
 *		special non-print keycodes returned by KeyBoard 
 *		  implementations
 *
 *	Functions:
 *		swritef - write strings with formatting to the terminal
 *		box_dr - draw an empty box with special chars on the terminal
 *
 *		popup functions: confirm_box, yesno_box, etc.
 *			
 *
 *
 *
 */
public class TermUtil{

	
	/*
	 *	Some high unicode drawing chars
	 *
	 */
	public static final char
		UL_CORNER =		'\u250F',
		UR_CORNER =		'\u2513',
		LL_CORNER =		'\u2517',
		LR_CORNER =		'\u251B',
		VERT_LINE =		'\u2503',
		HORZ_LINE =		'\u2501',
		SOLID_BLK =		'\u2588',
		SHADE_BLK = 		'\u2592',
		SMLE_FACE =		'\u263A',
		SAD_FACE =		'\u2639',
		NUKE =			'\u2622',
		SUN = 			'\u2600';


	/*
	 * Determines the color to push on the stack, and pushes it on the
	 * stack.  sptr is the pointer to the first char of the color def
	 * returns the new value of sptr.
	 * used by swritef
	 *
	 */
	private static int __do_col_push(LinkedStack<Color> stk, int sptr, 
			String s){
		char c = s.charAt(sptr);
		switch(c){
			case '#':
				int lhs = s.indexOf('#', sptr + 1);
				String cl = s.substring(sptr - 1, lhs+1);
				Color co = Util.str_to_col(cl);
				stk.push(co == null ? Color.WHITE : co);
				return lhs+1;
			case 'o':
				stk.push(Color.ORANGE);
				break;
			case 'g':
				stk.push(Color.GREEN);
				break;
			case 'r':
				stk.push(Color.RED);
				break;
			case 'y':
				stk.push(Color.YELLOW);
				break;
			case 'a':
				stk.push(Color.DARK_GRAY);
				break;
		}
		sptr++;
		return sptr;
	}
	
	public static void swritef(Terminal term, int x, int y, String s){
		swritef(term.get_curr_surface(), x, y, s);
	}
	
	public static void swritef(Surface surf, int x, int y, String s){
		swritef(surf, x, y, s, false);
	}

	/*
	 * This method puts a formatted string on term.
	 * Formating:
	 * 	- the '`' character is the formatting mark
	 * 	- rules:
	 * 		the formating mark, followed by a control character,
	 * 		begins or ends a formatting block. non-formatting chars
	 * 		do nothing.  Various characters following the ctrl char
	 * 		may give args to it
	 * 	-Control chars:
	 * 		'F': pushes a new foreground color, uses color defs
	 * 		'f': pops the current foreground color
	 * 		'B': pushes a new bg col, uses color defs
	 * 		'b': pops the bg col
	 * 		'K': starts blink
	 * 		'k': ends blink
	 * 		'A': pushes altcolor
	 * 		'a': pops altcolor
	 * 		's': blink uses the same char for altc
	 * 		'S': blink uses ' ' for altc
	 *
	 * 	-color defs:
	 * 		colors can be defined by color char and color nums
	 * 		-nums #r:g:b# where r,g,b are numbers from 0-255
	 * 		ex:	`F#35:143:2#
	 * 		-
	 *	
	 *	-newlines:
	 *		return to original x, y++
	 *	
	 *	-line wrapping:
	 *		if the string extends farther than the term area,
	 *		lines are not wrapped, extra is dropped
	 *
	 *
	 */
	public static void swritef(Surface surf, int x, int y, String s,
		       	boolean wrap){
		int sptr = 0;
		int xl = x, yl = y;
		char c = 0;
		LinkedStack<Color> bg_col = new LinkedStack<Color>(Color.BLACK);
		LinkedStack<Color> fg_col = new LinkedStack<Color>(Color.WHITE);
		LinkedStack<Color> alt_col =new LinkedStack<Color>(Color.BLACK);
		boolean altc_space = true, u_blink = false;
		while(sptr < s.length()){ //draw loop
			c = s.charAt(sptr);
			switch(c){ //desurfine if it is a normal char or special
				case '\n':
					yl++;
					xl = x;
					sptr++;
					break;
				case '`':
					sptr++;
					if(sptr >= s.length()){
						//ERROR, should not get this
						return;
					}
					c = s.charAt(sptr);
					switch(c){
						case 'B':
							sptr++;
							sptr = __do_col_push(
									bg_col,
									sptr,
									s);
							break;
						case 'F':
							sptr++;
							sptr = __do_col_push(
									fg_col, 
									sptr,
									s);
							break;
						case 'f':
							fg_col.pop();
							sptr++;
							break;
						case 'b':
							bg_col.pop();
							sptr++;
							break;
						case '`':
							surf.draw(xl, yl, '`',
								fg_col.peek(),
								bg_col.peek());
							sptr++;
							xl++;
							break;
						case 'K':
							u_blink = true;
							sptr++;
							break;
						case 'k':
							u_blink = false;
							sptr++;
							break;
						case 's':
							altc_space = false;
							sptr++;
							break;
						case 'S':
							altc_space = true;
							sptr++;
							break;
						case 'A':
							sptr++;
							sptr = __do_col_push(
								alt_col, sptr,
								s);
							break;
						case 'a':
							alt_col.pop();
							sptr++;
							break;
						default:
							//ERROR
							return;
					}
					break;
				default:
					if(!u_blink){
						surf.draw(xl, yl, c, 
							fg_col.peek(), 
							bg_col.peek());
					}else if(u_blink && altc_space){
						surf.draw(xl, yl, c, 
							fg_col.peek(),
							bg_col.peek(),
							true, ' ',
							alt_col.peek());
					}else if(u_blink && !altc_space){
						surf.draw(xl, yl, c, 
							fg_col.peek(),
							bg_col.peek(),
							true, c,
							alt_col.peek());
					}
					sptr++;
					xl++;
					if(wrap && xl > surf.get_w()){
						xl = x;
						yl++;
					}
			}
		}
	}

	public static void box_dr(Terminal term, int x, int y, int w, int h){
		box_dr(term.get_curr_surface(), x, y, w, h);
	}

	/*
	 * Draws an open box with the specified dimensions
	 * 	Uses special corner chars
	 */
	public static void box_dr(Surface surf, int x, int y, int w, int h){
		surf.draw(x, y, UL_CORNER);
		surf.draw(x + w, y + h, LR_CORNER);
	       	surf.draw(x + w, y, UR_CORNER);
		surf.draw(x, y + h, LL_CORNER);
		for(int xp = x + 1; xp < x + w; xp++){
			surf.draw(xp, y, HORZ_LINE);
			surf.draw(xp, y + h, HORZ_LINE);
		}
		for(int yp = y + 1; yp < y + h; yp++){
			surf.draw(x, yp, VERT_LINE);
			surf.draw(x + w, yp, VERT_LINE);
		}
	}

	public static void box_dr(Terminal term, int x, int y, int w, int h,
		Color fg, Color bg){
		box_dr(term.get_curr_surface(), x, y, w, h, fg, bg);
	}

	/*
	 * Draws an open box with the specified dimensions
	 * 	Uses special corner chars, uses fg and bg for colors
	 */
	public static void box_dr(Surface surf, int x, int y, int w, int h,
			Color fg, Color bg){
		surf.draw(x, y, UL_CORNER, fg, bg);
		surf.draw(x + w, y + h, LR_CORNER, fg, bg);
	       	surf.draw(x + w, y, UR_CORNER, fg, bg);
		surf.draw(x, y + h, LL_CORNER, fg, bg);
		for(int xp = x + 1; xp < x + w; xp++){
			surf.draw(xp, y, HORZ_LINE, fg, bg);
			surf.draw(xp, y + h, HORZ_LINE, fg, bg);
		}
		for(int yp = y + 1; yp < y + h; yp++){
			surf.draw(x, yp, VERT_LINE, fg, bg);
			surf.draw(x + w, yp, VERT_LINE, fg, bg);
		}
	}

	public static void fill_box(Terminal term, int x, int y, int w, int h,
			Color bg){
		fill_box(term.get_curr_surface(), x, y, w, h, bg);
	}

	public static void fill_box(Surface surf, int x, int y, int w, int h,
			Color bg){
		for(int xp = x; xp < x+w; xp++){
			for(int yp = y; yp < y+h; yp++){
				surf.draw(xp, yp, ' ', Color.WHITE, bg);
			}
		}
	}
	
	/*
	 * Sleeps until there is a key pressed
	 */
	public static void wait_for_input(Terminal term){
		term.clear_event();
		Key c = null;
		try{
			while(c == null){
				Thread.sleep(1000/60);
				c = term.peek_event();
			}
		}catch(Exception e){
			e.printStackTrace();
		}
	}

	private static String 
		blarr = "`K>`k",
		brarr = "`K<`k";
	private static String __ok_str = 
		"`Fy  " + blarr + "OK" + brarr + "  `f";


	/*
	 * Creates a 'ok' mesage box
	 * 	Args:
	 * 		term - terminal to use
	 * 		cp_bg - whether or not to copy the current show
	 * 			buffer for the background
	 * 		msg - Strings to display.  Do not use \n in them,
	 * 			use a new arg.
	 *
	 */
	public static void confirm_box(Terminal term, boolean cp_bg, String 
			... msg){
		//Setup drawing locations
		//Center in the middle of the screen
		int idx = Util.lng_str(msg);
		int mlen = msg[idx].length();
		int w = mlen + 4;
		int h = msg.length + 4;
		int x = term.get_x_len() / 2 - w / 2;
		int y = term.get_y_len() / 2 - h / 2;
		if(cp_bg){ //Copy from show to draw buffers
			term.copy_buff();
		}
		term.clear();
//		System.out.printf("x %d y %d w %d h %d\n", x, y, w, h);
		
		//Now draw the message box
		box_dr(term, x, y, w, h);
		y++;
		x += 2;
		for(String s : msg){
			swritef(term, x, y, s);
			y++;
		}
		x = term.get_x_len() / 2 - 3;
		y++;
		swritef(term, x, y, __ok_str);
		term.update();
		
		//Wait for awknowedge \n
		char c = 0;
		while(c != '\n'){
			wait_for_input(term);
			c = term.poll_event().c;
		}
//		System.out.println("Char: "+c);
	}
	
	private static String 
		__yes_nl =	"  `Fy"+blarr+"YES"+brarr+"`f    NO   ",
		__yes_nr =	"   YES    `Fy"+blarr+"NO"+brarr+"`f  ";



	public static boolean yesno_box(Terminal term, boolean cp_bg, String 
			... msg){
		boolean yes = false;
		//Positioning code
		int idx = Util.lng_str(msg);
		int mlen = msg[idx].length();
		mlen = mlen > 17 ? mlen : 17;
		int w = mlen + 4;
		int h = msg.length + 4;
		int x = term.get_x_len() / 2 - w / 2;
		int y = term.get_y_len() / 2 - h / 2;
		term.clear();
		if(cp_bg){ //Copy from show to draw buffers
			term.copy_buff();
		}
//		System.out.printf("x %d y %d w %d h %d\n", x, y, w, h);
		
		//Now draw the message box
		box_dr(term, x, y, w, h);
		y++;
		x += 2;
		for(String s : msg){
			swritef(term, x, y, s);
			y++;
		}
		x = term.get_x_len() / 2 - 8;
		y++;
		swritef(term, x, y, __yes_nr);
		term.update();
		//Watch for L-R arrows and enter
		char c = 0;
		while(c != '\n'){
			wait_for_input(term);
			c = term.poll_event().c;
			if(c == Key.K_LEFT || c == Key.K_RIGHT){
				yes = !yes;
				term.clear();
				term.copy_buff();
				if(yes){
					swritef(term, x, y, __yes_nl);
				}else{
					swritef(term, x, y, __yes_nr);
				}
				term.update();
			}
		}
		return yes;
		
	}


	/*
	 * Builds a menu. Returns the index of the item chosen
	 *
	 *	term -- terminal t use,
	 *	cp_bg -- to copy the show buffer for background
	 *	title -- the menu title, may include format chars safely
	 *	items -- the individual menu items, do not include 
	 *		format characters
	 *
	 */
	public static int menu_box(Terminal term, boolean cp_bg,
		       	String title, String items[]){
		int idx = 0;
		int ix = Util.lng_str(items);
		int mlen = items[ix].length();
		mlen = mlen > title.length() ? mlen : title.length();
		int w = mlen + 6;
		int h = items.length + 8;
		int x = term.get_x_len() / 2 - w / 2;
		int y = term.get_y_len() / 2 - h / 2;
		term.clear();
		if(cp_bg){ //Copy from show to draw buffers
			term.copy_buff();
		}
		//Now draw the message box
		fill_box(term, x, y, w, h, Color.DARK_GRAY);		
		box_dr(term, x, y, w, h, Color.WHITE, Color.DARK_GRAY);
		y++;
		x = term.get_x_len() / 2 - title.length() / 2;
		swritef(term, x, y, "`Ba"+title+"`b");
		y += 3;
		int yoff = y;
		for(int k = 0; k < items.length; k++){
			String s = items[k];
			x = term.get_x_len() / 2 - s.length() / 2;
			if (idx == k){
				x--;
				swritef(term, x, y, "`Ba`Fy"+blarr
						+s+brarr+"`f`b");
			}else{
				swritef(term, x, y, "`Ba"+s+"`b");
			}
			y++;
		}
		term.update();
		
		//now for the choosing code
		char c = 0;
		wait_for_input(term);
		boolean rd = false;
		while(c != '\n'){
			wait_for_input(term);
			c = term.poll_event().c;
			if(c == Key.K_UP){
				idx = --idx >= 0 ? idx : 0;
				rd = true;
			}else if(c == Key.K_DOWN){
				idx = ++idx < items.length ? idx : 
					items.length - 1;
				rd = true;
			}
			if(rd){
				rd = !rd;
				term.clear();
				term.copy_buff();
				y = yoff;
				for(int k = 0; k < items.length; k++){
					String s = items[k];
					x = term.get_x_len() / 2 - 
						(s.length() +2) / 2;
					if (idx == k){
						swritef(term, x, y, 
							"`Ba`Fy"+blarr+ s + 
							brarr+"`f`b");
					}else{
						swritef(term, x, y, 
							"`Ba " + s + " `b");
					}
					y++;
				}
				term.update();
			}
		}
		return idx;
	}

}
